import DebugExt from "../base/DebugExt";
import { EVENT_KEY_PLAY_AUDIO, IEventAudio, TAudioType } from "../ccExt/AudioExt";

/** 音效加载状态 */
const enum AudioLoadT {
    idle = 0,
    loading,
    success,
    failed
}

const { ccclass, property, menu } = cc._decorator;

/** 场景BGM控制文件 */
@ccclass
@menu('Audio/GameAudioPlayer')
export default class GameAudioPlayer extends cc.Component {

    // 默认背景音乐
    @property(cc.AudioSource)
    private readonly bgm: cc.AudioSource = null;
    /** 获取当前BGM */
    private get curBGMClip(): cc.AudioClip {
        if (!!this.bgm && !!this.bgm.clip && this.bgm.clip.isValid)
            return this.bgm.clip;
        else
            return null;
    }

    // 是否加载背景音乐目录
    @property
    private readonly loadBGM: boolean = false;
    @property({
        tooltip: "背景音乐文件夹在 resources 目录下的相对位置",
        visible() {
            return this.loadBGM;
        },
    })
    private readonly bgmPath: string = '';

    // 加载音效目录
    @property({
        tooltip: "音效文件夹在 resources 目录下的相对位置"
    })
    private readonly audioPath: string = 'Audio';


    protected onLoad(): void {
        this.preLoadAudio();

        if (!this.bgm) {
            DebugExt.log("默认背景音乐控件未创建！请确认本游戏是否不使用默认背景音乐。");
            // 支持无背景音乐模式
            // this._bgmLoadStatus = AudioLoadT.failed;
        }

        // 监听音效设置变化
        yy.event.on(this, yy.event.sys.SettingChanged, (e: yy.event.sys.SettingChanged) => {
            if (e.settingType == 1 || e.settingType == 2 || e.settingType == 6) {
                this.updateBGM();
            }
            if (e.settingType == 3 || e.settingType == 7) {
                this.updateAllEffectVolume();
            }
        });

        // 监听音效播放事件
        yy.event.on(this, yy.event.CustomEvent, this.onYYCustomEvent);
    }

    protected onDestroy(): void {
        yy.event.off(yy.event.CustomEvent, this.onYYCustomEvent);
        // 移除背景音效
        this.stopBGM();
        this.stopAllSubBgm();
    }

    /** 暂不支持音效停播 */
    private onYYCustomEvent(e: yy.event.CustomEvent) {
        if (e.eventID == EVENT_KEY_PLAY_AUDIO && !!e.param) {
            const data: IEventAudio = e.param as IEventAudio;
            if (!data) return;

            const audioName: string = data.audioName;
            const type: TAudioType | undefined = data.type;

            if (type == TAudioType.BGM && !!this.bgm) {
                // change BGM if it is exist
                if (this.curBGMClip.name != audioName && audioName.length > 0) {
                    const clip = this._bgmClips.get(audioName);
                    if (clip) {
                        this.bgm.clip = clip;
                        this._bgmPrecent = 0;
                        this.updateBGM();
                    }
                    else
                        DebugExt.warn("暂未加载到背景音乐", audioName);
                }
            }
            else if (type == TAudioType.Effect) {
                this.playEffect(audioName);
            }
            else if (type == TAudioType.Voice) {
                this.playVoice(audioName);
            }
            else if (type == TAudioType.SubBgm) {
                const stop: boolean = !!data.stop;
                if (stop)
                    this.stopSubBgm(audioName);
                else
                    this.playSubBgm(audioName);
            }
        }
    }

    private preLoadAudio(): void {
        // bgm
        if (!this.loadBGM && !!this.curBGMClip) {
            this._bgmClips.set(this.curBGMClip.name, this.curBGMClip);
            this._bgmLoadStatus = AudioLoadT.success;
        }
        else if (this.loadBGM && this.bgmPath.length > 0) {
            this.loadClips('BGM', this.bgmPath);
        }
        else
            this._bgmLoadStatus = AudioLoadT.failed;

        if (this.audioPath.length > 0) {
            this.loadClips('Audio', this.audioPath);
        }
        else
            this._audioLoadStatus = AudioLoadT.failed;
    }
    protected start(): void {
        this.updateBGM();
        this.updateAllEffectVolume();
    }

    /** 是否静音 */
    private get isMute(): boolean {
        return yy.setting.isMute || cc.game.isPaused();
    }
    /** 是否支持播放BGM */
    private get bgmOn(): boolean {
        return !this.isMute && yy.setting.isMusicEnable;
    }
    /** 是否支持播放音效--(当前游戏约定，不细分，采用静音选项判别) */
    private get effectOn(): boolean {
        return !this.isMute && yy.setting.isEffectEnable;
    }
    /** 是否支持播放人物语音--(当前游戏约定，不细分，采用静音选项判别) */
    private get voiceOn(): boolean {
        return !this.isMute && yy.setting.isVoiceEnable;
    }

    // 背景音乐启停
    private updateBGM() {
        if (this.bgmOn)
            this.playBGM();
        else
            this.stopBGM();
    }
    private playBGM(): void {
        if (!this.bgm || !this.bgm.clip || !this.bgmOn)
            return;
        if (this.bgm.enabled) {
            if (this._isBGMPlaying)
                this._bgmPrecent = this.bgm.getCurrentTime();
            this.bgm.play();
            this.bgm.volume = yy.setting.musicVolume;
            this.bgm.setCurrentTime(this._bgmPrecent);
            this._isBGMPlaying = true;
            DebugExt.log("【GameAudioPlayer】 播放背景音乐：", this.bgm.clip.name);
        }
    }
    private stopBGM(): void {
        if (!this.bgm || !this.bgm.clip || !this._isBGMPlaying)
            return;
        if (this.bgm.enabled) {
            this._bgmPrecent = this.bgm.getCurrentTime();
            this.bgm.stop();
            this._isBGMPlaying = false;
            DebugExt.log("【GameAudioPlayer】 停止背景音乐。");
        }
    }
    private _bgmPrecent: number = 0;
    private _isBGMPlaying = false;

    /**
     * 播放指定音效
     * @param name 音效名
     */
    private playEffect(name: string) {
        if (!this.effectOn || !this._audioClips || !name) return;
        const clip: cc.AudioClip = this._audioClips.get(name);
        this.playClip(clip, name);
    }

    /**
     * 播放人物语音(判断语种)
     * @param name 音效名
     */
    private playVoice(name: string) {
        if (!this.voiceOn || !this._audioClips || !name) return;
        const defLang = name + '_en';
        const curLang = name + '_' + yy.curLanguage;
        const clip = this._audioClips.get(curLang) || this._audioClips.get(defLang);
        this.playClip(clip, name);
    }

    // 播放指定clip
    private playClip(clip: cc.AudioClip, name: string) {
        if (!clip) {
            DebugExt.warn("GameAudioPlayer 暂未加载到音效:", name);
        }
        else {
            const id = cc.audioEngine.playEffect(clip, false);
            const volume = this.effectOn ? yy.setting.effectVolume : 0;
            cc.audioEngine.setVolume(id, volume);
            
            // 为音效生成唯一标识符（name + timestamp + id）
            const uniqueName = `${name}_${Date.now()}_${id}`;
            this._playingEffects.set(uniqueName, id);
            DebugExt.log("【GameAudioPlayer】 播放音效：", name, " 音量：", volume, " 唯一标识：", uniqueName);

            // 监听音效播放完成事件，完成后从map中移除
            cc.audioEngine.setFinishCallback(id, () => {
                this._playingEffects.delete(uniqueName);
            });
        }
    }
    /** 停止指定音效 */
    private stopClip(name: string) {
        DebugExt.log("##### stopClip:", name);
        if (!this._playingEffects || !name) return;
        
        // 查找第一个匹配的音效名称并停止它
        for (const entry of Array.from(this._playingEffects.entries())) {
            const [key, id] = entry;
            const originalName = key.split('_')[0];
            if (originalName === name) {
                cc.audioEngine.stopEffect(id);
                this._playingEffects.delete(key);
                break;
            }
        }
    }
    // 正在播放的音效列表
    private _playingEffects: Map<string, number> = new Map();
    private stopAllEffect(): void {
        // 创建一个数组副本以避免在迭代时修改Map
        const effectsToStop = Array.from(this._playingEffects.entries());
        effectsToStop.forEach(([uniqueName, id]) => {
            cc.audioEngine.stopEffect(id);
        });
        // 清空所有正在播放的音效
        this._playingEffects.clear();
    }


    /** 音效列表 */
    private _audioClips: Map<string, cc.AudioClip> = new Map();
    /** BGM列表 */
    private _bgmClips: Map<string, cc.AudioClip> = new Map();
    // 加载音效资源
    private loadClips(type: 'BGM' | 'Audio', path: string) {
        if (this.getClipsStatus(type) != AudioLoadT.idle) return;
        let map = type == 'BGM' ? this._bgmClips : this._audioClips;
        if (!map) return;
        this.setClipsStatus(type, AudioLoadT.loading);
        cc.loader.loadResDir(path, cc.AudioClip, (err, clips: cc.AudioClip[]) => {
            if (!err) {
                clips.forEach(clip => {
                    map.set(clip.name, clip);
                    DebugExt.log("加载音效资源成功, 路径：", path, clip.name);
                });
                this.setClipsStatus(type, AudioLoadT.success);
                if (type == 'BGM')
                    this.checkSubBgm();
            }
            else {
                DebugExt.warn("加载音效资源, 路径：", path, " 失败：", JSON.stringify(err));
                this.setClipsStatus(type, AudioLoadT.failed);
            }
            // end check
            this.updateBGM();
            this.updateAllEffectVolume();
        });
    }


    /** audio status */
    private get isAudioLoaded(): boolean {
        return this._audioLoadStatus == AudioLoadT.failed || this._audioLoadStatus == AudioLoadT.success;
    }
    private _audioLoadStatus: AudioLoadT = AudioLoadT.idle;
    /** BGM status */
    private get isBGMLoaded(): boolean {
        return this._bgmLoadStatus == AudioLoadT.failed || this._bgmLoadStatus == AudioLoadT.success;
    }
    private _bgmLoadStatus: AudioLoadT = AudioLoadT.idle;
    // 音效资源加载状态
    private getClipsStatus(type: 'BGM' | 'Audio'): AudioLoadT {
        return type == 'BGM' ? this._bgmLoadStatus : this._audioLoadStatus;
    }
    private setClipsStatus(type: 'BGM' | 'Audio', status: AudioLoadT) {
        if (type == 'BGM') {
            return this._bgmLoadStatus = status;
        }
        else {
            return this._audioLoadStatus = status;
        }
    }

    // 子背景音源列表
    private _subBgms: Map<string, number> = new Map();
    private stopAllSubBgm(): void {
        // 创建一个数组副本以避免在迭代时修改Map
        const subBgmsToStop = Array.from(this._subBgms.keys());
        subBgmsToStop.forEach(name => {
            this.stopSubBgm(name);
        });
    }
    // 等待播放的子背景音效
    private _waitSubBgms: Map<string, boolean> = new Map();
    private checkSubBgm(): void {
        // 创建一个数组副本以避免在迭代时修改Map
        const waitingSubBgms = Array.from(this._waitSubBgms.entries());
        waitingSubBgms.forEach(([name, play]) => {
            if (play) this.playSubBgm(name);
        });
        // 清空等待列表
        this._waitSubBgms.clear();
    }
    /**
     * 播放指定背景音效
     * @param name 音效名
     */
    private playSubBgm(name: string) {
        DebugExt.log("##### playSubBgm:", name);
        if (!this.isBGMLoaded) {
            this._waitSubBgms.set(name, true);
            DebugExt.log("##### 添加待播音效", name);
            return;
        }
        if (!this.effectOn || !this._bgmClips || !name) return;
        const clip: cc.AudioClip = this._bgmClips.get(name);
        if (!clip) {
            DebugExt.warn("GameAudioPlayer 暂未加载到音效:", name);
        }
        else {
            let id: number | undefined = this._subBgms.get(name);
            if (id == undefined || id < 0) {
                id = cc.audioEngine.playEffect(clip, true);
                this._subBgms.set(name, id);
            }
            const volume = this.effectOn ? yy.setting.effectVolume : 0;
            cc.audioEngine.setVolume(id, volume);
            DebugExt.log("【GameAudioPlayer】 播放子背景音效：", name, " 音量：", volume);
        }
    }
    /** 停止指定背景音效 */
    private stopSubBgm(name: string) {
        DebugExt.log("##### stopSubBgm:", name);
        if (!this.isBGMLoaded) {
            this._waitSubBgms.set(name, false);
            DebugExt.log("##### 移除待播音效", name);
            return;
        }
        if (!this._bgmClips || !name) return;
        const id: number | undefined = this._subBgms.get(name);
        if (id != undefined && id >= 0) {
            cc.audioEngine.stopEffect(id);
            this._subBgms.set(name, -1);
        }
    }
    /** 更改长音效音量 */
    private updateAllEffectVolume() {
        if (!this.effectOn) {
            this.stopAllSubBgm();
            this.stopAllEffect();
            DebugExt.log("##### 音效关闭，停止所有音效");
        }
    }
}
